﻿using System;
using System.Collections.Generic;
using System.Console;
using System.Drawing;
using System.Linq.Enumerable;
using Nemerle.Assertions;
using Nemerle.Collections;
using Nemerle.Text;
using Nemerle.Utility;

using Eco.ObjectRepresentation;

using CommonClasses.Model;

namespace CommandGraph.Model
{
  /// <summary>
  /// Инструмент, генерирующий LPMED-код для заданного графа команд
  /// </summary>
  public class CodeGenerationTool
  {
    private type nQueue[T] = Nemerle.Collections.Queue[T];
   
    //private functionOfTranstion[]
    private serviceProvider : IEcoServiceProvider = null;
    private compilationUnit : ICompilationUnit = null;
    
    public this(serviceProvider : IEcoServiceProvider, compilationUnit : ICompilationUnit)
    requires serviceProvider != null
    //requires compilationUnit != null
    {
      this.serviceProvider = serviceProvider;
      this.compilationUnit = compilationUnit;
    }
    
    public GenCode(source : CommandsGraph, code : out string) : ErrorReport
    {
      def GetUniqueVarName()
      {
        $"fakeVar_$(Guid.NewGuid().ToString().Map(fun(chr){| '-' => '_' | _ => chr}))"
      }
      code = string.Empty;
      
      mutable report = CheckGraph(source).Fold(ErrorReport(serviceProvider),
        fun(error, acc)
        {
          acc.Errors.Add(error);
          acc
        });
      
      when(report.Errors.Count == 0)
      {
        //очистим ID потоков - на случай, если граф уже компилировался
        source.Entities.Filter(_ is ActiveTransition)
          .Iter(fun(trans){(trans :> ActiveTransition).ThreadID = string.Empty});
          
        PlaceThreadID(source.Start);
        
        //source.Entities
        //  .Filter(fun(entity) {entity is ActiveTransition})
        //  .Map(fun(entity) {entity :> ActiveTransition})
        //  .Iter(fun(trans) {trans.Command += trans.ThreadID});
          
        //code = "I don't know how it can be done yet";
        code = "DEVICE CommandsGraph_" + source.GetHashCode().ToString() + eol +
        
        //переменные графа
        source.Variables.Fold("VAR ", fun(var, acc){acc + var.Name + ",\n"}) +
        GetUniqueVarName() + " : int;" + eol +
        
        //переменные для обслуживания условий
        source.Entities
          .Filter(_ is WaitVertex)
          .Map(_ :> WaitVertex)
          .Fold(string.Empty, fun(wait, acc) { $"$acc Expression_$(wait.GetHashCode()),\n"}) + 
        GetUniqueVarName() + ": bool;" + eol +
        
        "{" + eol +
        CodeSince(source.Start, GraphSort(source)) + eol +
        "}" + eol +
        "END.";
        code = FormatCStyleText(code);
      }
      
      report
    }
    
    private CheckGraph(graph : CommandsGraph) : list[Error]
    {
      def CheckVertices(vertices) : list[Error]
      {
        | [] => []
        | vertex :: tail => 
          {
            def ReportIfTrue(condition, message)
            {
              if (condition)
                Error(serviceProvider, 
                      message,
                      Point(),
                      compilationUnit,
                      vertex)
              else
                null
            }
            
            def report = ErrorReport(serviceProvider);
            
            (match(vertex)
            {
              | _ when vertex is StartVertex =>
                ReportIfTrue(vertex.OutcomingThreads.Count == 0, 
                  "Из вершины 'Начало' должен выходить хотя бы один переход")
              | _ when vertex is FinishVertex =>
                ReportIfTrue(vertex.IncomingThreads.Count == 0, 
                  "В вершину 'Конец' должен входить хотя бы один переход")
              | _ when vertex is WaitVertex => 
                ReportIfTrue((!(vertex.IncomingThreads.Count == 0) 
                    && (vertex.OutcomingThreads.Count == 0))
                  || ((vertex.IncomingThreads.Count == 0) 
                    && !(vertex.OutcomingThreads.Count == 0)),
                    "Несоответствие количества входящих и исходящих переходов")
              | _ when vertex is LoopCondition => 
              //ругаемся когда либо что-то входит, но ничгео не выходит, либо выходит, но ничего не входит
                ReportIfTrue(((vertex.IncomingThreads.Count > 1)
                    && (vertex.OutcomingThreads.Count < 2))
                  || ((vertex.IncomingThreads.Count < 2)
                    && (vertex.OutcomingThreads.Count > 1)),
                    "Несоответствие количества входящих и исходящих переходов")
              | _ when vertex is IfVertex =>
                {
                  def ifvert : IfVertex = vertex :> IfVertex;
                  def isGoesOut = (ifvert.YesBranch.Count() > 0)
                    && (ifvert.NoBranch.Count() > 0);
                  def condition = ((ifvert.IncomingThreads.Count == 0) && isGoesOut)
                    || ((ifvert.IncomingThreads.Count > 0) && !isGoesOut);
                  ReportIfTrue(condition,
                    "Несоответствие количества входящих и исходящих переходов")
                }
              | _ => null  
            }) :: CheckVertices(tail)
          }
      }
      
      CheckVertices(graph.Entities.Filter(_ is ForkAndJoin)
        .Map(_ :> ForkAndJoin))
        .Filter(fun(error){error != null})
    }

    private CodeSince(vertex : ForkAndJoin, vertexOrder : Hashtable[ForkAndJoin, int]) : string
    {
      mutable beginnings = nQueue([vertex]);
      mutable threadWithLastTransition = Nemerle.Collections.Hashtable();
      mutable threadWithFirstTransition = Nemerle.Collections.Hashtable();
      mutable result = string.Empty;
      mutable newConcurrent;
      mutable newThread;
      
      def transSortPredicate(first, second)
      { 
        vertexOrder[first.Ending.Parent :> ForkAndJoin] - vertexOrder[second.Ending.Parent :> ForkAndJoin]
      }
      def TransitionCode(transition)
      {
        $"$(transition.Command);$eol"
      }
      def WaitCode(wait)
      {
        match((wait :> WaitVertex).IsWaitingForFunction)
        {
          | false =>
            {
              def varname = $"Expression_$(wait.GetHashCode())";
              $"while($(wait.Expression)) { $varname = $(wait.Expression); }$eol"
            }
          | true => $"$(wait.Expression.Trim(' ', ';'));$eol"
        }
      }
      def LoopCode(loop)
      {
        $"while($(loop.Expression))$eol" +
        "{" + eol +
        CodeSince(
          match(loop.LoopBody.NestedEntities.Find(_ is StartVertex))
          {
            | None => null
            | some => some.Value
          } :> StartVertex, vertexOrder) + eol +
        "}" + eol
      }
      def RunThreads(threads, needEnqueue, parentThread)
      {
        def internalBeginnings = nQueue();
        
          def NewThread(firstTransition, needEnqueue, parentThread)
          {
            def threadName = $"Thread_$(firstTransition.GetHashCode())" +
              firstTransition.ThreadID.Map(fun(chr){ | '.' => '_' | _ => chr});
            
            def StopThread(transition, nameTruely)
            {
              def ending = transition.Ending.Parent :> ForkAndJoin;
              
              when((!beginnings.Contains(ending)) && needEnqueue)
                beginnings.Enqueue(ending);
              
              //threadWithLastTransition.Add(transition, threadName);
              threadWithLastTransition.Add(transition, 
                if (nameTruely) threadName else parentThread);
              
              string.Empty;
            }
            def CanContinueThread(vertex)
            {
              | _ when vertex is FinishVertex => false
              | _ when vertex is WaitVertex =>
                (vertex.IncomingThreads.Count == 1)
                  && (vertex.OutcomingThreads.Count == 1)
              | _ when vertex is LoopCondition =>
                (vertex.IncomingThreads.Count == 2) //один - от тела цикла, другой - рабочий
                  && (vertex.OutcomingThreads.Count == 2) //один - к телу цикла, второй - куда-нибудь
              | _ when vertex is IfVertex =>
                (vertex.IncomingThreads.Count == 1)
                  && ((vertex :> IfVertex).YesBranch.Count() == 1)
                  && ((vertex :> IfVertex).NoBranch.Count() == 1)
            }
            def AreChildrenFullyNested(children, id)
            {
              def IsFullyNested(transition)
              {
                def ending = transition.Ending.Parent :> ForkAndJoin;
                match(ending.IncomingThreads.Count)
                {
                  | 1 => ending.OutcomingThreads.Fold(true,
                    fun(trans, acc) {acc && IsFullyNested(trans)})
                  | more => ending.IncomingThreads
                      .Filter(fun(trans){trans.Beginning.Parent is ForkAndJoin})
                      .Fold(true,
                      fun(trans, acc) {acc && IDIsChildOf(IDFromString(trans.ThreadID), id)})
                }
              }
            
              children.Fold(true, fun(trans, acc) {acc && IsFullyNested(trans)})
            }
            def CanNestFork(vertex, id)
            {
              match(vertex)
              {
                | _ when vertex is LoopCondition =>
                  match(vertex.IncomingThreads.Count)
                  {
                    | 2 => AreChildrenFullyNested((vertex :> LoopCondition).NoBranch, id)
                    | more => false
                  }
                | _ when (vertex is IfVertex) || (vertex is WaitVertex) =>
                  match(vertex.IncomingThreads.Count)
                  {
                    | 1 => AreChildrenFullyNested(vertex.OutcomingThreads, id)
                    | more => false
                  }
                    
                | _ when vertex is FinishVertex => false
              }
            }
            def JoinIsNestable(vertex, id)
            {
              def AreIncomingInternal(incoming, id)
              {
                incoming.Fold(true, 
                  fun(trans, acc) 
                  {
                    acc && IDIsChildOf(IDFromString(trans.ThreadID), id)
                  })
              }

              match(vertex)
              {
                | _ when vertex is LoopCondition =>
                  (match(vertex.IncomingThreads.Count)
                  {
                    | 2 => true
                    | more => AreIncomingInternal(vertex.IncomingThreads
                        .Filter(fun(trans)
                        {
                          !ActiveTransition.ReferenceEquals(trans, (vertex :> LoopCondition).FromBody)
                        }), id)
                  }) && AreChildrenFullyNested((vertex :> LoopCondition).NoBranch, id)
                  
                | _ when (vertex is IfVertex) || (vertex is WaitVertex) =>
                  (match(vertex.IncomingThreads.Count)
                  {
                    | 1 => true
                    | more => AreIncomingInternal(vertex.IncomingThreads, id)
                  }) && AreChildrenFullyNested(vertex.OutcomingThreads, id)
                    
                | _ when vertex is FinishVertex => false
              }
            }
            def CanNestJoin(vertex, currentTrans)
            {
              def IsCurrentThreadLast(incoming)
              {
                incoming
                  .Fold(true, 
                    fun(trans, acc) { acc && threadWithLastTransition.Contains(trans) })
              }
              def id = IDFromString(currentTrans.ThreadID).Tail;
              match(vertex)
              {
                | _ when vertex is LoopCondition =>
                  (match(vertex.IncomingThreads.Count)
                  {
                    | 2 => true
                    | more => IsCurrentThreadLast(vertex.IncomingThreads
                        .Filter(fun(trans)
                        {
                          !ActiveTransition.ReferenceEquals(trans, (vertex :> LoopCondition).FromBody)
                        }))
                  }) && AreChildrenFullyNested((vertex :> LoopCondition).NoBranch, id)
                | _ when (vertex is IfVertex) || (vertex is WaitVertex) =>
                  {
                    def isLast = IsCurrentThreadLast(vertex.IncomingThreads);
                    def areChNested = AreChildrenFullyNested(vertex.OutcomingThreads, id);
                    Console.WriteLine($"CanNestJoin: $isLast $areChNested");
                    (match(vertex.IncomingThreads.Count)
                    {
                      | 1 => true
                      | more => isLast
                    }) && areChNested
                  }  
                | _ when vertex is FinishVertex => false
              }
            }
            
            def ContinueThread(transition)
            {
              def ending = transition.Ending.Parent :> ForkAndJoin;
              
              TransitionCode(transition) + 
              (match(ending)
              {
                | _ when CanContinueThread(ending) =>
                    match(ending)
                    {
                      | _ when ending is FinishVertex => ""
                      | wait when ending is WaitVertex =>  
                        WaitCode(wait :> WaitVertex) +
                        ContinueThread(wait.OutcomingThreads[0])
                      | loopVert when ending is LoopCondition =>
                        {
                          def headVertex = loopVert :> LoopCondition;
                          
                          LoopCode(headVertex) + 
                          ContinueThread(System.Linq.Enumerable.ElementAt(headVertex.NoBranch, 0))
                        }
                      | ifVert when ending is IfVertex =>
                        $"if ($((ifVert :> IfVertex).Expression)) then$eol" +
                        "{" + eol +
                        ContinueThread(ElementAt((ifVert :> IfVertex).YesBranch, 0)) + 
                        "}" + eol +
                        "else" + eol +
                        "{" + eol +
                        ContinueThread(ElementAt((ifVert :> IfVertex).NoBranch, 0)) + 
                        "}" + eol
                      | elseVertex => ""
                    }
                | _ when CanNestFork(ending, IDFromString(transition.ThreadID)) => 
                  newConcurrent(ending, needEnqueue, threadName)
                | _ when JoinIsNestable(ending, IDFromString(firstTransition.ThreadID).Tail) =>
                  {
                    _ = StopThread(transition, true);
                    when (CanNestJoin(ending, transition))
                    {
                      when(!needEnqueue && (!internalBeginnings.Contains(ending)))
                        internalBeginnings.Enqueue(ending);
                        //string.Empty//ставим во внутреннюю очередь
                    }
                    
                    string.Empty //откладываем обработку до лучших времён
                  }
                | _ => StopThread(transition, needEnqueue)
              })
            }
            
            threadWithFirstTransition.Add(firstTransition, threadName);
            
            def threadText = $"defun $(threadName)()$eol" +
            "{" + eol +
            ContinueThread(firstTransition) +
            "}" + eol;
            
            threadText
          }
        
        def processQueue(queue)
        {
          match(queue.Count)
          {
            | 0 => string.Empty
            | more => newConcurrent(queue.Dequeue(), needEnqueue, parentThread) + processQueue(queue)
          }
        }
        
        threads.Fold(string.Empty, //генерация
          fun(trans, acc){acc + NewThread(trans, needEnqueue, parentThread)}) + eol +

        threads.Fold(string.Empty, //запуск
          fun(trans, acc){$"$acc$(threadWithFirstTransition[trans])().go();$eol"}) +
          
        processQueue(internalBeginnings)
          
        //if (!needEnqueue) //в случае, когда нужно обарботать запуск и ожидание сразу
        //  threads.Fold(string.Empty, //ожидание
        //    fun(trans, acc){$"$acc$(threadWithFirstTransition[trans])().wait();$eol"});
        //else
        //  string.Empty;
      }
      def NewConcurrent(curBeginning, needEnqueue, parentThread)
      {
        //обрабатывать потомков нужно в топологическом порядке
        def outcoming = QuickSort_HAHA(List(curBeginning.OutcomingThreads
          .Filter(fun(trans) {trans.Ending.Parent is ForkAndJoin})),
          transSortPredicate);
          
        //ожидать необходимо не всегда
        (match(curBeginning.IncomingThreads.Count)
        {
          | _ =>
          curBeginning.IncomingThreads
            .Filter(fun(trans) {trans.Beginning.Parent is ForkAndJoin})
            .Filter(threadWithLastTransition.Contains(_))
            .Fold([],
              fun(trans, acc) 
              {
                if (!acc.Exists(fun(analog){threadWithLastTransition[trans] == threadWithLastTransition[analog]})) 
                  trans :: acc 
                else acc})
            .Fold(string.Empty, //ожидание входящих
              fun(trans, acc) {$"$acc$(threadWithLastTransition[trans])().wait();$eol"}) 
          /*
          | more when (more > 0) && (curBeginning.IncomingThreads
              .Filter(fun(trans) {trans.Beginning.Parent is ForkAndJoin})
              .Fold(true, 
                fun(trans, acc){ acc && threadWithLastTransition.Contains(trans) })) =>
              
              curBeginning.IncomingThreads.Fold(string.Empty, //ожидание входящих
                fun(trans, acc) {$"$acc$(threadWithLastTransition[trans])().wait();$eol"}) 
          | _ => string.Empty
          */
        })
        +     
        (match(curBeginning)
        {
          | _ when curBeginning is StartVertex => RunThreads(outcoming, needEnqueue, parentThread)
          | _ when curBeginning is FinishVertex => string.Empty
          | _ when curBeginning is WaitVertex => 
            WaitCode(curBeginning :> WaitVertex) +
            RunThreads(outcoming, needEnqueue, parentThread)
          | _ when curBeginning is LoopCondition =>
            {
              def headVertex = curBeginning :> LoopCondition;

              LoopCode(headVertex) + RunThreads(outcoming, needEnqueue, parentThread)
            }
          | _ when curBeginning is IfVertex =>
            {
              def ifvert = curBeginning :> IfVertex;
              
              def yesSorted = QuickSort_HAHA(List(ifvert.YesBranch),
                transSortPredicate);
              def noSorted = QuickSort_HAHA(List(ifvert.NoBranch),
                transSortPredicate);
              
              $"if ($(ifvert.Expression)) then" + eol +
              "{" + eol +
              RunThreads(yesSorted, false, parentThread) +
              "}" + eol +
              "else" + eol +
              "{" + eol +
              RunThreads(noSorted, false, parentThread) +
              "}" + eol
            }
        })
      }
      
      newConcurrent = NewConcurrent;
      
      while(beginnings.Count > 0)
      {
        result += NewConcurrent(beginnings.Dequeue(), true, "") 
      }
      
      result
    }
    
    private IDIsChildOf(child : list[int], parent : list[int]) : bool
    {
      GCID(child, parent) == parent
    }
    
    private IDFromString(source : string) : list[int]
    {
      source.Split('.')
        .Map(int.Parse(_))
        .ToListRev()
    }
    
    private IDToString(sourceid : list[int]) : string
    {
      sourceid.Tail.Fold(sourceid.Head.ToString(), 
        fun(cur, prev) {$"$cur.$prev"})
    }
    
    private GCID(first : list[int], second : list[int]) : list[int] //greatest common id
    {
      def MaxSubsequence(first, second)
      {
        | ([], []) => []
        | ([], _) => []
        | (_, []) => []
        | (_, _) =>
          if (int.Equals(first.Head, second.Head))
            first.Head :: MaxSubsequence(first.Tail, second.Tail)
          else
            []
      }
      
      MaxSubsequence(first.Rev(), second.Rev()).Rev()
    }
      
    private PlaceThreadID(vertex : ForkAndJoin) : void
    {
      mutable newConcurrent;
      mutable usedIDs = [];
     
      def filterActive(trans){trans.Beginning.Parent is ForkAndJoin}
      
      def NewSuccessive(transition : ActiveTransition, id)
      {
        def ending = transition.Ending.Parent;
        when (ending is ForkAndJoin)
        {
          (transition :> ActiveTransition).ThreadID = IDToString(id);
          newConcurrent(ending :> ForkAndJoin)
        }
      }
      def NewConcurrent(vertex)
      {
        def processNext(id : list[int])
        {
          def processCollection(children)
          {
            _ = children.Fold(0, 
              fun(trans, i) { NewSuccessive(trans, i :: id); i + 1 })
          }
          match(vertex)
          {
            | _ when vertex is FinishVertex => {}
            | _ when vertex is StartVertex => processCollection(vertex.OutcomingThreads)
            | _ when vertex is WaitVertex =>
              {
                match(vertex.OutcomingThreads.Count)
                {
                  | 1 => NewSuccessive(vertex.OutcomingThreads[0], id)
                  | more => processCollection(vertex.OutcomingThreads)
                }
              }
            | _ when vertex is LoopCondition =>
              {
                def condition = vertex :> LoopCondition;
                
                match(vertex.OutcomingThreads.Count)
                {
                  | 2 => NewSuccessive(System.Linq.Enumerable.ElementAt(condition.NoBranch, 0), id)
                  | more => processCollection(condition.NoBranch)
                }
                
                PlaceThreadID(
                  match(condition.LoopBody
                    .NestedEntities
                    .Find(fun(entity) {entity is StartVertex}))
                  {
                    | None => null 
                    | some => some.Value :> StartVertex
                  });
              }
            | _ when vertex is IfVertex => 
              {
                def ifvertex = vertex :> IfVertex;
                def processBranch(branch)
                {
                  match(branch.Count())
                  {
                    | 1 => NewSuccessive(System.Linq.Enumerable.ElementAt(branch, 0), id)
                    | more => processCollection(branch);
                  }
                }
                
                processBranch(ifvertex.YesBranch);
                processBranch(ifvertex.NoBranch);
              }
          }
          
        }

        def incoming = vertex.IncomingThreads.Filter(filterActive);

        match(incoming.Count())
        {
          | 0 => processNext([0])
          | 1 => processNext(IDFromString(incoming.Head.ThreadID))
          | 2 when vertex is LoopCondition => 
            processNext(IDFromString(
              (match(incoming
                .Find(fun(trans) 
                  {!ActiveTransition.ReferenceEquals(trans, (vertex :> LoopCondition).FromBody)}))
                {
                  | None => null
                  | some => some.Value
                }).ThreadID))
          | more => 
            {
              def canContinue = incoming
                .Fold(true, 
                   fun(trans, value) {value && (trans.ThreadID != string.Empty)});

              when (canContinue)
              {
                def incomingIDs = incoming.Map(fun(trans){IDFromString(trans.ThreadID)});

                usedIDs = usedIDs.Filter(fun(id){incomingIDs.Contains(id)});

                def suggestedID = incoming.Fold(IDFromString(incoming.Head.ThreadID),
                  fun(trans, prevGCID)
                  { GCID(IDFromString(trans.ThreadID), prevGCID) });
                    
                def possibleID = usedIDs
                  .Filter(_ == suggestedID.Tail)
                  .Sort(fun(a, b){ a.Head - b.Head })
                  .Fold(suggestedID,
                  fun(exitsting, id)
                  {
                    if (suggestedID == exitsting)
                      (Math.Min(suggestedID.Head, exitsting.Head) + 1) :: suggestedID.Tail
                    else
                      suggestedID
                  });
                usedIDs ::= possibleID;
                processNext(possibleID);
              }
            }
        }
      }
      newConcurrent = NewConcurrent;
      NewConcurrent(vertex);
    }
    
    /// <summary>
    /// Топологическая сортировка,
    /// применяется при кодогенерации для определения порядка обработки потомков.
    /// Данная сортировка сводит сравнение вершин к сравнению чисел.
    /// </summary>
    /// <param name="graph">
    /// Граф, вершины которого сортируются
    /// </param>
    /// <returns>
    /// Таблицу вида (Вершина, Число).
    /// Число - эквивалент соответствующей вершины при сравнении.
    /// </returns>
    private GraphSort(graph : CommandsGraph) 
      : Hashtable[ForkAndJoin, int]
    {
      mutable result = Hashtable();
      mutable helpInfo = Hashtable(graph.Entities
        .Filter(_ is ForkAndJoin)
        .Map(fun(vertex)
          {(vertex :> ForkAndJoin, (vertex :> ForkAndJoin)
            .IncomingThreads
            .Filter(fun(trans){trans.Beginning.Parent is ForkAndJoin})
            .Count())}));
      mutable pretenders = nQueue(helpInfo.Keys
        .Filter(fun(key){helpInfo[key] == 0}));
      
      while (pretenders.Count > 0)
      {
        def current = pretenders.Dequeue();
        result[current] = result.Count();
        current.OutcomingThreads
          .Filter(fun(trans){trans.Ending.Parent is ForkAndJoin})
          .Iter(
            fun(trans)
            { 
              def child = trans.Ending.Parent :> ForkAndJoin;
              helpInfo[child]--;
              when(helpInfo[child] == 0)
                pretenders.Enqueue(child);
            });
      }
      
      result
    }
    
    private QuickSort_HAHA[T](values : IList[T], compare : (T * T) -> int)
      : IList[T]
    {
      mutable result = List(values);
      for(mutable i = 1; i < values.Count; i++)
        for(mutable j = 1; j < values.Count; j++)
          when(compare(result[j - 1], result[j]) > 0)
            result[j - 1] <-> result[j];
            
      result
    }
    
    private FormatCStyleText(text : string) : string
    {
      def tab(depth)
      {
        | 0 => string.Empty
        | _ => "\t" + tab(depth - 1)
      }
      
      def result = text.Fold((string.Empty, 0),
        fun(chr, acc)
        {
          match(chr)
          {
            | '\n' => (acc[0] + chr.ToString() + tab(acc[1]), acc[1])
            | '{' => (acc[0] + chr.ToString(), acc[1] + 1)
            | '}' => (acc[0] + chr.ToString(), acc[1] - 1)
            | _ => (acc[0] + chr.ToString(), acc[1])
          }
        });
        
      result[0].Replace("\t}", "}")
    }
    
    //private duplicate(str : string, n : int) : string 
    //  {| 0 => "" | _ => str + duplicate(str, n-1)}
    //private  eol(n : int) : string {duplicate("\r\n")};
    //private  tab(n : int) : string {duplicate("\t")};
    //private eol : string = "\t";
    private eol : string = "\r\n";
  }
}
